home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastep4.asm < prev    next >
Assembly Source File  |  1995-01-01  |  15KB  |  392 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play 8bit DMA mode for SoundBlaster v1.00
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16/SBPRO2/SB2.0, TESTPHASE ON SB1.0
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / 4..23khz (no highspeed - but on SB16 4..44kHz)
  9. ; Parameters  : none
  10. ; Notes:
  11. ;     ■ pause/continue fails on SB2.0,SB1.5,SB1.0 (and all below :)
  12. ;     ■ To creat a 8 bit mono unsigned file do :   "VOC2RAW TEST1.VOC /I"
  13. ;
  14. ; ■ DSP command 48h  ... set DMA block size
  15. ; ■ DSP command 1Ch  ... play 8bit mono autoinit
  16. ; ■ DSP command 40h  ... set sample rate
  17. ; ■ DSP command D1h  ... Enable Speaker
  18. ; ■ DSP command D3h  ... Disable Speaker
  19. ; ■ DSP command D0h  ... Halt Autoinit 8 bit DMA operation
  20. ; ■ DSP command D4h  ... Continue Autoinit 8 bit DMA operation
  21. ;
  22.  
  23. .MODEL small
  24. .286
  25.  
  26. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  27.  
  28. ; SoundBlaster SETUP
  29. BASEADDR           EQU 0220h       ;SoundBlaster base address
  30. IRQ7               EQU 15          ;SoundBlaster IRQ
  31. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  32.  
  33. ; PIC MASKS FOR MASK/DEMASK IRQ
  34. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  35. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  36.  
  37. ; DMA CONTROLLER REGISTERS :
  38. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  39. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  40. CLEARFLIPFLOP      EQU 00ch
  41. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  42. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMA 1
  43. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  44.  
  45. ; SAMPLERATE : (if you change it pay attention to maximum samplerate)
  46. TIMECONST          EQU 165          ; = 10989 Hz (256-1000000/11000)
  47.  
  48. ; DMA WRITE MODE
  49. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  50.  
  51. ;──────────────────────────────────────────────────────────────────────────────
  52. ; MACRO DEFINITIONs
  53. ;──────────────────────────────────────────────────────────────────────────────
  54. STARTUP                 MACRO
  55. ; MASM 5.x COMPATIBILITY
  56. __start:                mov     ax,DGROUP
  57.                         mov     ds,ax
  58.                         mov     bx,ss
  59.                         sub     bx,ax
  60.                         shl     bx,004h
  61.                         mov     ss,ax
  62.                         add     sp,bx
  63. ENDM
  64.  
  65. WAITWRITE               MACRO
  66. LOCAL                   loopWait,endloop
  67. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  68. ;          Returns   : n/a
  69. ;          Destroys  : AL
  70.  
  71.                         push    cx
  72.                         xor     cx,cx           ; need that for slow SBs !
  73. loopWait:               dec     cx
  74.                         jz      endloop
  75.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  76.                         or      al,al
  77.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  78. endloop:                pop     cx
  79. ENDM
  80.  
  81. WAITREAD                MACRO
  82. LOCAL                   loopWait,endloop
  83. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  84. ;          Returns   : n/a
  85. ;          Destroys  : AL
  86.  
  87.                         push    cx
  88.                         xor     cx,cx           ; need that for slow SBs !
  89. loopWait:               dec     cx
  90.                         jz      endloop
  91.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  92.                         or      al,al
  93.                         jns     loopWait        ; Jump if bit7=0 - no data available
  94. endloop:                pop     cx
  95. ENDM
  96.  
  97. RESET_DSP               MACRO
  98. local                   SBthere
  99. ;          Arguments : n/a
  100. ;          Returns   : n/a
  101. ;          Destroys  : DX,AL
  102.  
  103.                         mov      dx,BASEADDR+06h
  104.                         mov      al,1
  105.                         out      dx,al          ; start DSP reset
  106.  
  107.                         in       al,dx
  108.                         in       al,dx
  109.                         in       al,dx
  110.                         in       al,dx          ; wait 3 µsec
  111.  
  112.                         xor      al,al
  113.                         out      dx,al          ; end DSP Reset
  114.  
  115.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  116.                         WAITREAD
  117.                         sub      dx,4           ; dx = DSP Read Data
  118.                         in       al,dx
  119.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  120.                         je       SBthere
  121.                         jmp      RESET_ERROR    ; No SB - exit program
  122. SBthere:
  123. ENDM
  124. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  125.  
  126. .STACK 100h
  127.  
  128. .DATA
  129. ;──────────────────────────────────────────────────────────────────────────────
  130. ; TWO COPIES FOR PAGE OVERRIDE REASONS :
  131.  
  132. SAMPLEBUFFER LABEL BYTE
  133.     INCLUDE TEST1.INC         ; FIRST COPY OF SAMPLE SOUND
  134. SAMPLEBUFFEREND LABEL BYTE
  135.     INCLUDE TEST1.INC         ; SECOND COPY OF SAMPLE SOUND
  136.  
  137.     part                db 1
  138.  
  139.     information         db 13,10,'DMASTEP4.EXE - repeat sound again and again'
  140.                         db 13,10,'Pause playing with key "p" and continue it then with any key.'
  141.                         db 13,10,'Stop playing with <ESC>.','$'
  142.     txtpart0            db 13,10,'playing part 0','$'
  143.     txtpart1            db 13,10,'playing part 1','$'
  144.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  145.  
  146.     OLDInterruptSEG     dw ?
  147.     OLDInterruptOFS     dw ?
  148.  
  149.     positionLo          db ?
  150.     positionHi          db ?
  151.  
  152.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  153. ;──────────────────────────────────────────────────────────────────────────────
  154. .CODE
  155.  STARTUP
  156.  
  157.            RESET_DSP
  158.  
  159.            ; WRITE INFORMATION TO SCREEN :
  160.            mov     dx,offset information
  161.            mov     ah,9
  162.            int     21h                         ; write program information to screen
  163.  
  164.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  165.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  166.            WAITWRITE
  167.            mov     al,0D1h                     ; AL = Enable speaker
  168.            out     dx,al                       ; Output: DSP Write Data or Command
  169.  
  170.            ; SETUP IRQ :
  171.            xor     ax,ax
  172.            mov     es,ax                       ; es to page 0 (Interrupt table)
  173.            mov     si,IRQ7*4                   ; si = position in interrupt table
  174.  
  175.            ; DISABLE IRQ
  176.            in      al,021h
  177.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  178.            out     021h,al
  179.  
  180.            ; CHANGE POINTER IN INTERRUPT TABLE
  181.            mov     ax,es:[si]
  182.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  183.            mov     ax,OFFSET OWN_IRQ
  184.            mov     es:[si],ax                  ; set offset of new interrupt routine
  185.            mov     ax,es:[si+2]
  186.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  187.            mov     ax,cs
  188.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  189.  
  190.            ; CHANGE PIC MASK :
  191.            in      al,021h
  192.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  193.            out     021h,al
  194.  
  195. ;──────────────────────────────────────────────────────────────────────────────
  196. ; calculate page and offset for DMAcontroller :
  197. ;
  198. ; segment*16+offset - 20bit memory location -> upper 4 bits  = page
  199. ;                                              lower 16 bits = offset
  200. ;──────────────────────────────────────────────────────────────────────────────
  201.            mov     si,offset samplebuffer
  202.            mov     cx,SAMPLEBUFFERLENGTH-1
  203.  
  204.            mov     ax,ds
  205.            rol     ax,4                ; * 16 - higher 4 bits in al
  206.            mov     bl,al
  207.            and     bl,00fh             ; BL - higher 4 bits
  208.            and     al,0f0h             ; clear higher 4bits in AL
  209.            add     si,ax               ; SI = offset
  210.            adc     bl,0                ; BL = page
  211. ;──────────────────────────────────────────────────────────────────────────────
  212. ; check for DMApage override :
  213. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  214. ; transfer data is placed in one page - no page overrides are allowed
  215. ;──────────────────────────────────────────────────────────────────────────────
  216. ; To solve that :
  217. ; creat a DMA buffer with double size you want - if the first part is placed
  218. ; on a page border the second part is for sure not
  219. ;──────────────────────────────────────────────────────────────────────────────
  220.            neg     si          ; si = 65536 - si   (bytes left to DMA page border)
  221.            cmp     si,cx       ; if si (bytes left to border) > cx (bytes to play)
  222.            ja      nooverride  ; then there's no page override
  223.  
  224.            ; WE HAVE TO USE SECOND PART
  225.            neg     si          ; si = offset of first part
  226.            add     si,cx       ; si = si + length of one part
  227.            inc     si          ; si=si+1 - start of second part
  228.            inc     bl          ; second part is then on the next page
  229.            neg     si          ; look at the next command ;)
  230.                                ; (that is better than a jump ?)
  231. nooverride:
  232.            neg     si
  233.  
  234. ;──────────────────────────────────────────────────────────────────────────────
  235. ; Setup DMA-controller :
  236. ;
  237. ; 1st  MASK DMA CHANNEL
  238. ;
  239.            mov     al,DMAchannel
  240.            add     al,4
  241.            out     WRITEMASK,al
  242. ;──────────────────────────────────────────────────────────────────────────────
  243. ; 2nd  CLEAR FLIPFLOP
  244. ;
  245.            out     CLEARFLIPFLOP,al
  246. ;──────────────────────────────────────────────────────────────────────────────
  247. ; 3rd  WRITE TRANSFER MODE
  248. ;
  249.            mov     al,WANTEDMODE
  250.            add     al,DMAchannel
  251.            out     WRITEMODE,al
  252. ;──────────────────────────────────────────────────────────────────────────────
  253. ; 4th  WRITE PAGE NUMBER
  254. ;
  255.            mov     al,bl
  256.            out     PAGE_CHN,al
  257. ;──────────────────────────────────────────────────────────────────────────────
  258. ; 5th  WRITE BASEADDRESS
  259. ;
  260.            mov     ax,si
  261.            out     BASE_CHN,al
  262.            mov     al,ah
  263.            out     BASE_CHN,al
  264. ;──────────────────────────────────────────────────────────────────────────────
  265. ; 6th  WRITE SAMPLELENGTH-1
  266. ;
  267.            mov     al,cl
  268.            out     COUNT_CHN,al
  269.            mov     al,ch
  270.            out     COUNT_CHN,al
  271. ;──────────────────────────────────────────────────────────────────────────────
  272. ; 7th  DEMASK CHANNEL
  273. ;
  274.            mov     al,DMAchannel
  275.            out     WRITEMASK,al
  276.  
  277. ;──────────────────────────────────────────────────────────────────────────────
  278. ; Setup SoundBlaster :
  279. ;
  280. ; 1st  SET TIMECONSTANTE
  281. ;
  282.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  283.            WAITWRITE
  284.            mov     al,040h                     ;AL = Set timeconstant
  285.            out     dx,al
  286.            WAITWRITE
  287.            mov     al,TIMECONST
  288.            out     dx,al
  289.  
  290. ;──────────────────────────────────────────────────────────────────────────────
  291. ; 2nd   use 8bit mono sound (autoinit/no highspeed - DSPC 1Ch)
  292. ;
  293.            ; SETUP SIZE
  294.            WAITWRITE
  295.            mov     al,048h                     ;AL = DMA DAC 8bit
  296.            out     dx,al
  297.            mov     cx,SAMPLEBUFFERLENGTH
  298.            shr     cx,1                        ; generate IRQ every half buffer
  299.            dec     cx
  300.            WAITWRITE
  301.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  302.            out     dx,al
  303.            WAITWRITE
  304.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  305.            out     dx,al
  306.  
  307.            ; SETUP PLAYMODE
  308.            WAITWRITE
  309.            mov     al,1ch                      ;AL = Auto-Initialize DMA DAC, 8-bit
  310.            out     dx,al
  311.  
  312. ; NOW TRANSFER ..... :)
  313.  
  314. waitloop:  mov     ah,01                       ;AH = Check for character function
  315.            int     016h                        ;   Interrupt: Keyboard
  316.            jz      waitloop
  317.            xor     ah,ah                       ;Read character, flush keypress
  318.            int     016h                        ;   Interrupt: Keyboard
  319.            cmp     al,'p'
  320.            je      pause
  321.            cmp     al,27                       ; check for <ESC>
  322.            jne     waitloop
  323.            jmp     exit
  324. pause:     ; now halt it :
  325.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  326.            WAITWRITE
  327.            mov     al,0D0h
  328.            out     dx,al
  329.  
  330.            xor     ah,ah                       ;Read character, flush keypress
  331.            int     016h                        ;   Interrupt: Keyboard
  332.  
  333.            WAITWRITE
  334.            mov     al,0d4h
  335.            out     dx,al
  336.  
  337.            jmp     waitloop
  338.  
  339. exit:      ; FIRST HALT DMA TRANSFER (reset is the easiest way) :
  340.            RESET_DSP
  341.  
  342.            ; RESTORE PIC MASK
  343.            in      al,021h
  344.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  345.            out     021h,al
  346.  
  347.            ; RESTORE IRQ :
  348.            xor     ax,ax
  349.            mov     es,ax                       ; es to page 0 (Interrupt table)
  350.            mov     si,IRQ7*4
  351.            mov     ax,[OLDInterruptOFS]
  352.            mov     es:[si],ax                  ; set old interrupt routine
  353.            mov     ax,[OLDInterruptSEG]
  354.            mov     es:[si+2],ax
  355.  
  356.            ; TERMINATE EXE:
  357. return2dos:
  358.            mov     ax,04c00h
  359.            int     21h
  360.  
  361. ; display information if Soundblaster is not on this baseaddress
  362. RESET_ERROR:
  363.            mov     dx,offset sberror
  364.            mov     ah,9
  365.            int     21h                         ; text output
  366.            jmp     return2dos
  367.  
  368. ;──────────────────────────────────────────────────────────────────────────────
  369. ; Our own IRQ for detecting buffer half SB currently plays
  370. ; It's generated by the SoundBlaster hardware
  371. ;──────────────────────────────────────────────────────────────────────────────
  372. OWN_IRQ:
  373.            pusha
  374.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  375.            in      al,dx
  376.            mov     ax,@data
  377.            mov     ds,ax
  378.            mov     dx,offset txtpart0
  379.            cmp     [part],0
  380.            je      notpart1
  381.            mov     dx,offset txtpart1
  382. notpart1:  mov     ah,9
  383.            int     21h             ; text output
  384.            neg     [part]
  385.            inc     [part]          ; part = 1-part  result : 0,1,0,1,0,....
  386.            mov     al,020h
  387.            out     020h,al         ;ACKNOWLEDGE HARDWARE INTERRUPT
  388.            popa
  389.            IRET
  390.  
  391. END     __start
  392.